package com.pubinfo.passbook.engine.utils;

import com.pubinfo.passbook.common.exception.ServiceException;
import com.pubinfo.passbook.common.model.http.engine.ComputeParam;
import com.pubinfo.passbook.common.model.http.engine.ComputeParamSlot;
import com.pubinfo.passbook.engine.Computor;
import com.pubinfo.passbook.engine.cell.ComputeUnit;
import com.pubinfo.passbook.engine.constants.ComputeChainType;
import com.pubinfo.passbook.engine.process.ParamenterProcessor;
import org.apache.commons.lang3.StringUtils;

import java.util.Collection;
import java.util.stream.Collectors;

/**
 * 计算引擎的实用校验工具
 *
 * <p>
 * 泛型在运行时会被擦除, 需要手动做字段的计算类型校验来保证最中生成的计算链是正确的.
 * 重复的校验可能会被做好几次， 也无所谓了。
 * </p>
 * <p>
 * 这个工具类中的大部分方法都不会向外部抛出异常。异常需要在外部处理
 *
 * @author zhouch
 * @date 2021/12/16 8:40
 */
public final class ValidateUtils {


    /**
     * 计算器检查是否属于目标计算链类型
     *
     * @param computor
     * @param targetChainType
     * @return
     */
    public static Boolean checkChainType(final Computor<?, ?> computor, final String targetChainType) {
        String computorChainType = computor.getChainType();

        return computorChainType.contentEquals(targetChainType) || computorChainType.contentEquals(ComputeChainType.COMMON);
    }

    /**
     * 计算器检查是否属于目标计算链类型
     *
     * @param computors
     * @param targetChainType
     * @return
     */
    public static Boolean checkChainType(Collection<? extends Computor<?, ?>> computors, String targetChainType) {

        long availCount = computors.stream().filter(t -> checkChainType(t, targetChainType)).count();

        return availCount == computors.size();
    }

    /**
     * 计算器检查是否属于目标计算链类型 (不包含通用类型)
     *
     * @param computor
     * @param targetChainType
     * @return
     */
    public static Boolean checkChainTypeOnly(final Computor<?, ?> computor, final String targetChainType) {
        String computorChainType = computor.getChainType();

        return computorChainType.contentEquals(targetChainType);
    }


    /**
     * 计算器检查是否属于目标计算链类型 (不包含通用类型)
     *
     * @param computors
     * @param targetChainType
     * @return
     */
    public static Boolean checkChainTypeOnly(final Collection<? extends Computor<?, ?>> computors, String targetChainType) {
        long availCount = computors.stream().filter(t -> checkChainTypeOnly(t, targetChainType)).count();

        return availCount == computors.size();
    }


    /**
     * 检查计算结果类型是否属于目标类型
     *
     * @param computor
     * @param resultType
     * @return
     */
    public static Boolean checkResultType(final Computor<?, ?> computor, final String resultType) {
        String computorResultType = computor.getResultType();

        return computorResultType.contentEquals(resultType);
    }

    /**
     * 检查计算结果类型是否属于目标类型
     *
     * @param computors
     * @param resultType
     * @return
     */
    public static Boolean checkResultType(Collection<? extends Computor<?, ?>> computors, String resultType) {
        long availCount = computors.stream().filter(t -> checkResultType(t, resultType)).count();

        return availCount == computors.size();
    }

    /**
     * 检查计算链和计算结果类型是否属于预期
     *
     * @param computor
     * @param targetChainType
     * @param resultType
     * @return
     */
    public static Boolean checkChainTypeAndResultType(Computor<?, ?> computor, final String targetChainType, final String resultType) {
        return checkChainType(computor, targetChainType) && checkResultType(computor, resultType);
    }

    /**
     * 检查计算单元中参数的可用性
     *
     * @return
     */
    public static Boolean checkComputeUnitParamsAvailable(ComputeUnit<?, ?> unit, Collection<ComputeParam> params) {
        Collection<ComputeParamSlot> slots = unit.getParamSlots();

        long availCount = slots.stream().filter(t -> {
            // 标记为, 参数是否可用
            boolean flag = false;

            for (ComputeParam param : params) {
                // 如果参数名称和参数结果类型匹配则通过
                if (param.getName().contentEquals(t.getName()) && param.getResultType().contentEquals(t.getType())) {
                    flag = true;
                }
            }

            return flag;
        }).count();

        return availCount == slots.size();
    }

    /**
     * 检查计算单元中参数处理器的可用性
     *
     * @return
     */
    public static Boolean checkComputeUnitProcessorAvailable(ComputeUnit<?, ?> unit,
                                                             Collection<? extends ParamenterProcessor<?, ?>> processors) {
        Collection<ComputeParamSlot> slots = unit.getParamSlots();

        String unitChainType = unit.getChainType();

        long availCount = slots.stream().filter(t -> {
            // 标记为, 参数是否可用
            boolean flag = false;

            String slotName = t.getName();
            String slotType = t.getType();

            for (ParamenterProcessor<?, ?> processor : processors) {

                String paramName = processor.getPlaceHolderName();
                String paramType = processor.getResultType();

                // 如果参数名称, 参数结果类型, 计算链类型匹配则通过
                if (
                        paramName.contentEquals(slotName) &&
                                paramType.contentEquals(slotType) &&
                                checkProcessorChainType(processor, unitChainType)) {
                    flag = true;
                }
            }

            if (!flag) {

                // 参数处理器信息
                String processorsInfo = processors.stream().map(ParamenterProcessor::toString).collect(Collectors.joining());

                throw new ServiceException("没有找到对应插槽需要的参数处理器信息: 插槽参数名称: " + "slotName" + "\n"
                        + "插槽参数类型: " + slotType
                        + "计算单元计算链类型:" + unitChainType
                        + "当前所有的参数处理器信息: " + processorsInfo);
            }

            return flag;
        }).count();

        return availCount == slots.size();
    }


    /**
     * 检查计算参数元信息列表和参数处理器列表是否可用和匹配
     *
     * @return
     */
    public static Boolean checkComputeParamProcessorAvailable(Collection<ComputeParam> paramInfos,
                                                              Collection<? extends ParamenterProcessor> processors) {
        long matchCount = paramInfos.stream().filter(t -> {
            boolean flag = false;

            String paramName = t.getName();
            String paramType = t.getType();
            String paramResultType = t.getResultType();

            for (ParamenterProcessor<?, ?> processor : processors) {

                String processorName = processor.getPlaceHolderName();
                String processorType = processor.getType();
                String processorResultType = processor.getResultType();

                if (
                        StringUtils.equals(processorResultType, paramResultType)
                                && StringUtils.equals(processorType, paramType)
                                && StringUtils.equals(processorName, paramName)
                ) {
                    flag = true;
                }
            }

            if (!flag) {
                throw new ServiceException("参数列表和参数处理器不匹配: 参数名: "
                        + paramName
                        + " 参数类型: "
                        + paramType
                        + "参数返回结果信息: "
                        + paramResultType
                        + "参数处理器信息: "
                        + processors.toString());
            }

            return flag;
        }).count();

        return matchCount == paramInfos.size();
    }

    /**
     * 检查参数处理器的计算链类型是否对应
     *
     * @return
     */
    public static Boolean checkProcessorChainType(ParamenterProcessor<?, ?> processor, String targetChainType) {
        String paramChainType = processor.getChainType();
        return paramChainType.contentEquals(targetChainType)
                || paramChainType.contentEquals(ComputeChainType.COMMON)
                || targetChainType.contentEquals(ComputeChainType.COMMON);
    }


}
